import Macro from "../../core/macro";
import { Subject } from "../../core/subject";
import { Waiter } from "../../core/utils"; 
import { Protocol, ProtocolFactory } from "./protocol_dist";

export class Network {
    static state = "";
    static socket: WebSocket = null;
    static listenerMap: Map<new () => Protocol, Subject> = new Map();

    static async Init(url: string) {
        if (this.socket) {
            return;
        }
        let waiter = new Waiter<void>();
        this.state = "ready";
        this.socket = new WebSocket(url);
        this.socket.onopen = (ev: any): any => {
            this.state = "connected";
            waiter.finish();
        };
        this.socket.onmessage = (ev: MessageEvent) => {
            let msg: Protocol = null;
            try {
                msg = ProtocolFactory.CreateFromMixed(JSON.parse(ev.data));
            }
            catch (e) {
                console.warn("recv:", e);
            }

            if (msg) {
                // console.log("recv:", msg.toMixed());
            }
            else {
            }
            this.Dispatch(msg);
        };
        this.socket.onclose = () => {
            waiter.finish();
            this.state = "closed";
            this.socket = null;
        };
        this.socket.onerror = () => {
            waiter.finish();
            this.state = "error";
            this.socket = null;
        };
        return await waiter.await();
    }
    static AddListener<T extends Protocol>(msgCtor: new () => T, func: (msg: T, eventName: string) => void, master: any) {
        let subject = this.listenerMap.get(msgCtor);
        if (!subject) {
            subject = new Subject;
            this.listenerMap.set(msgCtor, subject);
        }
        subject.on("message", func, master);
    }
    static AddListenerOnce<T extends Protocol>(msgCtor: new () => T, func: (msg: T, eventName: string) => void, master: any) {
        let subject = this.listenerMap.get(msgCtor);
        if (!subject) {
            subject = new Subject;
            this.listenerMap.set(msgCtor, subject);
        }
        subject.once("message", func, master);
    }
    static DelListener<T extends Protocol>(msgCtor: new () => T, func: (msg: T, eventName: string) => void, master: any) {
        let subject = this.listenerMap.get(msgCtor);
        if (!subject) {
            subject = new Subject;
            this.listenerMap.set(msgCtor, subject);
        }
        subject.off<T>(master, "message", func);
    }
    static TargetOff(master: any) {
        this.listenerMap.forEach(subject => {
            subject.off(master);
        });
    }
    static Dispatch(msg) {
        let subjectList = this.listenerMap.get(msg.constructor);
        if (subjectList) {
            subjectList.emit("message", msg);
        }
    }
    static Send(msg: Protocol) {
        let mix = msg.toMixed();
        // console.log("send:", mix);
        this.socket.send(JSON.stringify(mix));
    }
    static async SendAndWaitRecv<T extends Protocol>(msg: Protocol, recvCtor: new () => T) {
        let mix = msg.toMixed();
        // console.log("send:", mix);
        this.socket.send(JSON.stringify(mix));
        let waiter = new Waiter<T>();
        this.AddListenerOnce(recvCtor, (rtn) => {
            waiter.finish(rtn);
        }, this);
        return await waiter.await();
    }
};

window["net"] = Network;